package sip

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
	"sync"

	"gitee.com/larry_wan/go-lib/siprocket"
)

type TransHandle interface {
    HandleRequest(*Request, *siprocket.SipMsg, *Transport)
    HandleResponse(string, *Response)
}

type Transport struct {
    Handler    TransHandle
    Protocol   string
    RemoteIP   string
    RemotePort uint
    UDPConn    *net.UDPConn
    TCPConn    *net.TCPConn
    TcpWLock   sync.RWMutex
    TcpRWriter *bufio.ReadWriter

    Closed bool
}

func (transport *Transport) Close() {
	transport.Closed = true
	if transport.UDPConn != nil && transport.UDPConn.RemoteAddr() != nil {
		transport.UDPConn.Close()
	}
	if transport.TCPConn != nil {
		transport.TCPConn.Close()
	}
}

func (transport *Transport) Write(buf []byte) (n int, err error) {
	if transport.Protocol == "UDP" {
		remoteAddr := transport.UDPConn.RemoteAddr()
		if remoteAddr == nil {
			remoteAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", transport.RemoteIP, transport.RemotePort))
			if err != nil {
				return
			}
			n, err = transport.UDPConn.WriteTo(buf, remoteAddr)
		} else {
			n, err = transport.UDPConn.Write(buf)
		}
	} else {
		remoteAddr := transport.TCPConn.RemoteAddr()
		if remoteAddr == nil {
			return
		}
		//ret, err := transport.TCPConn.Write(buf)
		transport.TcpWLock.Lock()
		// bufLen := make([]byte, 2)
		// binary.BigEndian.PutUint16(bufLen, uint16(n))
		// transport.TcpRWriter.Write(bufLen)
		n, err = transport.TcpRWriter.Write(buf)
		transport.TcpRWriter.Flush()
		transport.TcpWLock.Unlock()
	}

	return
}

func (transport *Transport) Read() {
	if transport.Protocol == "UDP" {
		bufUDP := make([]byte, UDP_BUF_SIZE)
		for !transport.Closed {
			n, _, err := transport.UDPConn.ReadFromUDP(bufUDP)
			if err != nil {
				continue
			}
			sipBuf := bufUDP[:n]
			sipRaw := string(sipBuf)
			sipMsg := siprocket.Parse(sipBuf)
			statusCode := string(sipMsg.Req.StatusCode)
			contentLen, _ := strconv.Atoi(string(sipMsg.ContLen.Value))
			header := sipRaw[:len(sipRaw)-contentLen]
			body := sipRaw[len(sipRaw)-contentLen:]
			Logf("transport udp接收：[%s]<<<<<<[%s]\r\n%v", transport.UDPConn.LocalAddr().String(), transport.UDPConn.RemoteAddr().String(), sipRaw)
			if statusCode == "" { // request
				req, err := UnmarshalRequest(header, body)
				if err != nil {
					log.Printf("unmarshal request failed, %v", err)
					continue
				}
				req.FixVia(transport)
				transport.Handler.HandleRequest(req, &sipMsg, transport)
			} else { // response
				res, err := UnmarshalResponse(header, body)
				if err != nil {
					log.Printf("unmarshal response failed, %v", err)
					continue
				}
				callid := res.HeaderMap["Call-ID"]
				cseq := res.HeaderMap["CSeq"]
				transport.Handler.HandleResponse(fmt.Sprintf("%s:%s", callid, cseq), res)
			}
		}
	} else {
		connRW := bufio.NewReadWriter(bufio.NewReader(transport.TCPConn), bufio.NewWriter(transport.TCPConn))
		bufLen := make([]byte, 2)
		for !transport.Closed {
			if _, err := io.ReadFull(connRW, bufLen); err != nil {
				log.Printf("read tcp sip length bytes error, %v", err)
				transport.TCPConn.Close()
				break
			}
			rtpLen := binary.BigEndian.Uint16(bufLen)
			rtpBytes := make([]byte, rtpLen)
			if _, err := io.ReadFull(connRW, rtpBytes); err != nil {
				log.Printf("read tcp sip bytes error, %v", err)
				transport.TCPConn.Close()
				break
			}
			sipBuf := rtpBytes
			sipRaw := string(sipBuf)
			sipMsg := siprocket.Parse(sipBuf)
			statusCode := string(sipMsg.Req.StatusCode)
			contentLen, _ := strconv.Atoi(string(sipMsg.ContLen.Value))
			header := sipRaw[:len(sipRaw)-contentLen]
			body := sipRaw[len(sipRaw)-contentLen:]
			Logf("transport tcp接收：[%s]<<<<<<[%s]\r\n%v", transport.TCPConn.LocalAddr().String(), transport.TCPConn.RemoteAddr().String(), sipRaw)
			if statusCode == "" { // request
				req, err := UnmarshalRequest(header, body)
				if err != nil {
					log.Printf("unmarshal request failed, %v", err)
					continue
				}
				req.FixVia(transport)
				transport.Handler.HandleRequest(req, &sipMsg, transport)
			} else { // response
				res, err := UnmarshalResponse(header, body)
				if err != nil {
					log.Printf("unmarshal response failed, %v", err)
					continue
				}
				callid := res.HeaderMap["Call-ID"]
				cseq := res.HeaderMap["CSeq"]
				transport.Handler.HandleResponse(fmt.Sprintf("%s:%s", callid, cseq), res)
			}
		}
	}
}
